<?php

namespace App\Service;

use Illuminate\Support\Facades\Cache;

/**
 * 代理Ip
 * https://www.kuaidaili.com/uc/dps/?orderid=911169750935821&tab=Fetch-ip
 *
 * 获取令牌
 * https://www.kuaidaili.com/doc/api/get_secret_token/
 *
 */
class ProxyIp
{
    protected $secretId;
    protected $secretKey;

    // 初始化
    public function __construct()
    {
        // 账户API密钥
        $secretId  = "udsrhgug995rrjo1ppij";
        $secretKey = "yr00iclv1h5kkpfu96k8dhr3q1m9jvfc";
        // 订单API密钥
        $orderId   = "911169750935821";
        $secretId  = 'ob4tn5ryobn7iruaaahi';
        $secretKey = "yhsaez2egwwdxnk2d7d30fta5i2buf5v";

        $this->secretId  = $secretId;
        $this->secretKey = $secretKey;
    }

    // 获取一条并检测Ip 私密Ip
    public function getOneCheckDpsIp($time, $times = 0)
    {
        $url    = 'https://dps.kdlapi.com/api/getdps';
        $param  = [
            'secret_id'  => $this->secretId,
            'secret_key' => $this->secretKey,
            'pt'         => '1',
            'sep'        => '1',
            'timestamp'  => $time,
            'nonce'      => round(1000, 10000),
            'num'        => 1,
            'sign_type'  => 'token',
            "format"     => 'json',
        ];
        $param3 = $this->getParams($url, $param);

        // 获取Ip
        $curl = new Curl();
        $res  = $curl->url($url)->post($param3)->request();
        $data = json_decode($res, true);
        if(empty($data)){
            return '';
        }

        // 获取失败 则重新获取
        if($data['code'] != 0){
            // 如果是token过期
            if($data['code'] == -107){
                $this->getToken(true);
            }

            // 如果次数过多 则直接返回
            if($times > 5){
                return '';
            }

            return $this->getOneCheckDpsIp($time, $times + 1);
        }

        // 获取成功 则检测IP
        $ips    = $data['data']['proxy_list'];
        $proxys = $this->checkDpsIps($ips);
        $proxy  = '';
        foreach($proxys as $ip => $check){
            if($check){
                $proxy = 'http://'.$ip;
            }
        }

        // 如果检测失败 则重新获取
        if(empty($proxy)){
            // 如果次数过多 则直接返回
            if($times > 5){
                return '';
            }

            return $this->getOneCheckDpsIp($time, $times + 1);
        }

        return $proxy;
    }

    // 获取Ip 私密Ip
    public function getDpsIps($time, $num = 1)
    {
        $url    = 'https://dps.kdlapi.com/api/getdps';
        $param  = [
            'secret_id'  => $this->secretId,
            'secret_key' => $this->secretKey,
            'pt'         => '1',
            'sep'        => '1',
            'timestamp'  => $time,
            'nonce'      => round(1000, 10000),
            'num'        => $num,
            'sign_type'  => 'token',
            "format"     => 'json',
        ];
        $param3 = $this->getParams($url, $param);

        // 获取Ip
        $curl = new Curl();
        $res  = $curl->url($url)->post($param3)->request();
        $data = json_decode($res, true);
        if(empty($data)){
            return [];
        }

        // 获取成功
        if($data['code'] == 0){
            return $data['data']['proxy_list'];
        }

        return $this->getDpsIps($time, $num);
    }

    // 检测Ip 私密Ip
    public function checkDpsIps($proxy = [])
    {
        if(is_array($proxy)){
            $proxy = implode(',', $proxy);
        }

        $url    = 'https://dps.kdlapi.com/api/checkdpsvalid';
        $param  = ['secret_id' => $this->secretId, 'sign_type' => 'token', 'proxy' => $proxy,];
        $param3 = $this->getParams($url, $param);

        // 获取Ip
        $curl = new Curl();
        $res  = $curl->url($url)->post($param3)->request();
        $data = json_decode($res, true);
        if(empty($data)){
            return [];
        }

        // 获取成功
        if($data['code'] == 0){
            return $data['data'];
        }

        return $this->checkDpsIps($proxy);
    }

    // 获取签名
    public function makeSignature($method, $url, $type = 'sha1', $param = [])
    {
        list(, $path) = explode(".com", $url);

        $secretKey = $this->secretKey;
        $rawStr    = strtoupper($method).$path.'?'.$this->arrayToString($param);
        $signStr   = base64_encode(hash_hmac($type, $rawStr, $secretKey, true));

        return $signStr;
    }

    // 获取令牌
    public function getToken($is_force = false)
    {
        $name  = 'proxy_ip_token';
        $token = Cache::get($name);
        if(empty($token) || $is_force){
            $url   = 'https://auth.kdlapi.com/api/get_secret_token';
            $param = ['secret_id' => $this->secretId, 'secret_key' => $this->secretKey,];
            $curl  = new Curl();
            $res   = $curl->url($url)->post($param)->request();
            $data  = json_decode($res, true);
            if(empty($data)){
                return '';
            }
            if($data['code'] == 0){
                $token = $data['data']['secret_token'];
                Cache::put($name, $token, 3500);
            } else{
                return $this->getToken();
            }
        }

        return $token;
    }

    // 数组转字符串
    public function arrayToString($param)
    {
        ksort($param);
        $rawStr = '';
        $comm   = '';
        foreach($param as $key => $str){
            $rawStr .= $comm.$key.'='.$str;
            $comm   = '&';
        }

        return $rawStr;
    }


    // 请求参数整理
    protected function getParams($url, $args = array())
    {
        $params["secret_id"] = $this->secretId;
        $setIpWhitelist      = "dev.kdlapi.com/api/setipwhitelist"; # 设置IP白名单
        # 参数args是必须的
        if(isset($args) && is_array($args)){
            foreach($args as $key => $value){
                $params["$key"] = $value;
            }
        } else{
            return "_get_params miss keywords \n";
        }


        switch($params["sign_type"]){
            case "":
                break;
            case "simple":
                $params['signature'] = $this->secretKey;
                break;
            case "token":
                $params['signature'] = $this->getToken();
                break;
            case "hmacsha1":
                $params['timestamp'] = (int)time();
                if(strpos($url, $setIpWhitelist) !== false){
                    $params["signature"] = $this->makeSignature("POST", $url, "sha1", $params);
                } else{
                    $params["signature"] = $this->makeSignature("GET", $url, "sha1", $params);
                }
                break;
            default:
                throw new KdlNameError("unknown sign_type".$params["sign_type"]);
        }

        return $params;
    }

}
